package com.hopes.bizz.close.gis.service.impl;

import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.hopes.bizz.close.common.constant.HazardDicConstant;
import com.hopes.bizz.close.common.constant.ParkCloseProperty;
import com.hopes.bizz.close.gis.service.ClosedHazardService;
import com.hopes.bizz.close.gis.vo.hazard.*;
import com.hopes.bizz.close.visitor.entity.ClosedHazardCarApplyEntity;
import com.hopes.bizz.close.visitor.service.ClosedHazardCarApplyService;
import com.hopes.bizz.common.core.constant.CommonConstant;
import com.hopes.bizz.common.core.constant.CommonEnum;
import com.hopes.bizz.common.core.util.DateUtils;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author yumr
 * @ProjectName bizz
 * @Description 危险品监管一张图接口
 * @time 2024/2/26 18:15
 */
@Service
@RequiredArgsConstructor
public class ClosedHazardServiceImpl implements ClosedHazardService {

    private final ParkCloseProperty parkCloseProperty;
    private final ClosedHazardCarApplyService closedHazardCarApplyService;

    @Override
    public ApplyCountVO queryHazardApplyCount() {
        final Date time = Calendar.getInstance().getTime();
        final List<ClosedHazardCarApplyEntity> hazardCarApplyList = closedHazardCarApplyService.list(
                Wrappers.<ClosedHazardCarApplyEntity>lambdaQuery().ge(ClosedHazardCarApplyEntity::getApplyTime, time));
        final Map<Integer, Long> auditLongMap = hazardCarApplyList.stream().filter(e -> ObjUtil.isNotNull(e.getAuditStatus())).collect(Collectors.groupingBy(ClosedHazardCarApplyEntity::getAuditStatus, Collectors.counting()));

        final int notAudit = auditLongMap.getOrDefault(HazardDicConstant.CLOSE_FLOW_BILL_AUDIT_STATUS_NOT_AUDIT, 0L).intValue();
        final int passAudit = auditLongMap.getOrDefault(HazardDicConstant.CLOSE_FLOW_BILL_AUDIT_STATUS_PASS_AUDIT, 0L).intValue();
        final int unPassAudit = auditLongMap.getOrDefault(HazardDicConstant.CLOSE_FLOW_BILL_AUDIT_STATUS_NOT_PASS_AUDIT, 0L).intValue();
        final AuditCountVO auditCountVO = new AuditCountVO(notAudit, passAudit, unPassAudit);

        final Map<String, Long> statusLongMap = hazardCarApplyList.stream().filter(e -> StrUtil.isNotBlank(e.getStatus())).collect(Collectors.groupingBy(ClosedHazardCarApplyEntity::getStatus, Collectors.counting()));
        final int notEnter = statusLongMap.getOrDefault(HazardDicConstant.CLOSE_FLOW_BILL_STATUS_NOT_ENTER, 0L).intValue();
        final int hasEnter = statusLongMap.getOrDefault(HazardDicConstant.CLOSE_FLOW_BILL_STATUS_HAS_ENTER, 0L).intValue();
        final int hasOuter = statusLongMap.getOrDefault(HazardDicConstant.CLOSE_FLOW_BILL_STATUS_HAS_OUTER, 0L).intValue();
        final EnterOuterCountVO enterOuterCountVO = new EnterOuterCountVO(notEnter, hasEnter, hasOuter);

        return new ApplyCountVO(hazardCarApplyList.size(), auditCountVO, enterOuterCountVO);
    }

    @Override
    public ApplyCountChartVO queryHazardApplyCountChart(String beginTimeStr, String endTimeStr) {
        final ApplyCountChartVO applyCountChartVO = new ApplyCountChartVO();
        final Date endTime = DateUtils.parseDate(CommonEnum.StartEndEnum.END, endTimeStr);
        final Date beginTime = DateUtils.parseDate(CommonEnum.StartEndEnum.START, beginTimeStr);

        int calendar = Calendar.HOUR_OF_DAY;
        Function<Date, String> timeHandleMapper = DateUtils::limitHour;
        if (DateUtils.getIntervalDays(beginTime, endTime) > parkCloseProperty.getGis().getChartHourToDay()) {
            calendar = Calendar.DAY_OF_YEAR;
            timeHandleMapper = DateUtils::limitDay;
        }
        final List<String> indexList = DateUtils.handlerDateTime(beginTime, endTime, timeHandleMapper, calendar, 1);

        final List<ClosedHazardCarApplyEntity> hazardCarApplyList = closedHazardCarApplyService.list(
                Wrappers.<ClosedHazardCarApplyEntity>lambdaQuery().ge(ClosedHazardCarApplyEntity::getApplyTime, beginTime).le(ClosedHazardCarApplyEntity::getApplyTime, endTime));

        applyCountChartVO.getAuditCountChartList().addAll(buildAuditCountChartList(indexList, timeHandleMapper, hazardCarApplyList));
        applyCountChartVO.getEnterOuterCountChartList().addAll(buildEnterOuterCountChartList(indexList, timeHandleMapper, hazardCarApplyList));
        return applyCountChartVO;
    }

    @Override
    public ApplyCarTypeVO queryHazardApplyCarType(String beginTimeStr, String endTimeStr) {
        if (StrUtil.isBlank(beginTimeStr) || StrUtil.isBlank(endTimeStr)) {
            final Date time = Calendar.getInstance().getTime();
            endTimeStr = DateUtils.limitDay(time);
            beginTimeStr = DateUtils.limitDay(time);
        }
        return new ApplyCarTypeVO(queryHazardApplyCarTypeChart(beginTimeStr, endTimeStr));
    }

    @Override
    public List<ApplyCompanyVO> queryHazardApplyCompanyChart(String beginTimeStr, String endTimeStr) {
        if (StrUtil.isBlank(beginTimeStr) || StrUtil.isBlank(endTimeStr)) {
            final Date time = Calendar.getInstance().getTime();
            endTimeStr = DateUtils.limitDay(time);
            beginTimeStr = DateUtils.limitDay(time);
        }
        return queryHazardApplyCompanyChartList(beginTimeStr, endTimeStr);
    }

    private List<ApplyCompanyVO> queryHazardApplyCompanyChartList(String beginTimeStr, String endTimeStr) {
        final Date endTime = DateUtils.parseDate(CommonEnum.StartEndEnum.END, endTimeStr);
        final Date beginTime = DateUtils.parseDate(CommonEnum.StartEndEnum.START, beginTimeStr);

        final List<ClosedHazardCarApplyEntity> hazardCarApplyList = closedHazardCarApplyService.list(
                Wrappers.<ClosedHazardCarApplyEntity>lambdaQuery().ge(ClosedHazardCarApplyEntity::getApplyTime, beginTime).le(ClosedHazardCarApplyEntity::getApplyTime, endTime));

        return hazardCarApplyList.stream().filter(e ->
                ObjUtil.isNotNull(e.getOrgId())).collect(Collectors.groupingBy(ClosedHazardCarApplyEntity::getOrgId, Collectors.counting()))
                .entrySet().stream().map(e -> new ApplyCompanyVO(e.getKey(), e.getValue().intValue())).collect(Collectors.toList());
    }

    private List<ApplyCarTypeChartVO> queryHazardApplyCarTypeChart(String beginTimeStr, String endTimeStr) {
        final Date endTime = DateUtils.parseDate(CommonEnum.StartEndEnum.END, endTimeStr);
        final Date beginTime = DateUtils.parseDate(CommonEnum.StartEndEnum.START, beginTimeStr);

        int calendar = Calendar.HOUR_OF_DAY;
        Function<Date, String> timeHandleMapper = DateUtils::limitHour;
        if (DateUtils.getIntervalDays(beginTime, endTime) > parkCloseProperty.getGis().getChartHourToDay()) {
            calendar = Calendar.DAY_OF_YEAR;
            timeHandleMapper = DateUtils::limitDay;
        }
        final List<String> indexList = DateUtils.handlerDateTime(beginTime, endTime, timeHandleMapper, calendar, 1);

        final List<ClosedHazardCarApplyEntity> hazardCarApplyList = closedHazardCarApplyService.list(
                Wrappers.<ClosedHazardCarApplyEntity>lambdaQuery().ge(ClosedHazardCarApplyEntity::getApplyTime, beginTime).le(ClosedHazardCarApplyEntity::getApplyTime, endTime));

        return buildApplyCarTypeChartList(indexList, timeHandleMapper, hazardCarApplyList);
    }

    private List<ApplyCarTypeChartVO> buildApplyCarTypeChartList(List<String> indexList, Function<Date, String> timeHandleMapper, List<ClosedHazardCarApplyEntity> hazardCarApplyList) {
        final List<ApplyCarTypeChartVO> resList = new ArrayList<>();
        for (String index : indexList) {
            final Map<String, Long> longMap = hazardCarApplyList.stream().filter(e -> ObjUtil.isNotNull(e.getApplyTime()))
                    .filter(e -> index.equals(timeHandleMapper.apply(e.getApplyTime()))).collect(Collectors.groupingBy(ClosedHazardCarApplyEntity::getCompanyCar, Collectors.counting()));
            final int companyCarNum = longMap.getOrDefault(CommonConstant.boolean_true_string, 0L).intValue();
            final int notCompanyCarNum = longMap.getOrDefault(CommonConstant.boolean_false_string, 0L).intValue();
            resList.add(new ApplyCarTypeChartVO(index, companyCarNum, notCompanyCarNum));
        }
        return resList;
    }


    private <T> Collection<? extends AuditCountChartVO> buildAuditCountChartList(List<String> indexList, Function<Date, String> timeHandleMapper, List<ClosedHazardCarApplyEntity> hazardCarApplyList) {
        final List<AuditCountChartVO> resList = new ArrayList<>();
        for (String index : indexList) {
            final Map<Integer, Long> longMap = hazardCarApplyList.stream()
                    .filter(e -> ObjUtil.isNotNull(e.getApplyTime()))
                    .filter(e -> index.equals(timeHandleMapper.apply(e.getApplyTime()))).collect(Collectors.groupingBy(ClosedHazardCarApplyEntity::getAuditStatus, Collectors.counting()));
            final int notAudit = longMap.getOrDefault(HazardDicConstant.CLOSE_FLOW_BILL_AUDIT_STATUS_NOT_AUDIT, 0L).intValue();
            final int passAudit = longMap.getOrDefault(HazardDicConstant.CLOSE_FLOW_BILL_AUDIT_STATUS_PASS_AUDIT, 0L).intValue();
            final int unPassAudit = longMap.getOrDefault(HazardDicConstant.CLOSE_FLOW_BILL_AUDIT_STATUS_NOT_PASS_AUDIT, 0L).intValue();
            resList.add(new AuditCountChartVO(index, notAudit, passAudit, unPassAudit));
        }
        return resList;
    }

    private Collection<? extends EnterOuterCountChartVO> buildEnterOuterCountChartList(List<String> indexList, Function<Date, String> timeHandleMapper, List<ClosedHazardCarApplyEntity> hazardCarApplyList) {
        final List<EnterOuterCountChartVO> resList = new ArrayList<>();
        for (String index : indexList) {
            final Map<String, Long> statusLongMap = hazardCarApplyList.stream()
                    .filter(e -> ObjUtil.isNotNull(e.getApplyTime()))
                    .filter(e -> index.equals(timeHandleMapper.apply(e.getApplyTime()))).collect(Collectors.groupingBy(ClosedHazardCarApplyEntity::getStatus, Collectors.counting()));
            final int notEnter = statusLongMap.getOrDefault(HazardDicConstant.CLOSE_FLOW_BILL_STATUS_NOT_ENTER, 0L).intValue();
            final int hasEnter = statusLongMap.getOrDefault(HazardDicConstant.CLOSE_FLOW_BILL_STATUS_HAS_ENTER, 0L).intValue();
            final int hasOuter = statusLongMap.getOrDefault(HazardDicConstant.CLOSE_FLOW_BILL_STATUS_HAS_OUTER, 0L).intValue();

            resList.add(new EnterOuterCountChartVO(index, notEnter, hasEnter, hasOuter));
        }
        return resList;
    }
}
